/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Xml.Linq;
using STSdb.Data;
using STSdb.Files;
using STSdb.General.Buffers;

namespace STSdb.Persist
{
    public static class PersistMap
    {
        private static Dictionary<Type, Type> map = new Dictionary<Type, Type>();

        static PersistMap()
        {
            //primitive types
            map[typeof(Boolean)] = typeof(BooleanIndexerPersist);
            map[typeof(Char)] = typeof(CharDeltaIndexerPersist);
            map[typeof(SByte)] = typeof(SByteDeltaIndexerPersist);
            map[typeof(Byte)] = typeof(ByteDeltaIndexerPersist);
            
            map[typeof(Int16)] = typeof(Int16DeltaIndexerPersist);
            map[typeof(UInt16)] = typeof(UInt16DeltaIndexerPersist);
            map[typeof(Int32)] = typeof(Int32DeltaIndexerPersist);
            map[typeof(UInt32)] = typeof(UInt32DeltaIndexerPersist);
            map[typeof(Int64)] = typeof(Int64DeltaIndexerPersist);
            map[typeof(UInt64)] = typeof(UInt64DeltaIndexerPersist);

            map[typeof(Single)] = typeof(SingleDeltaIndexerPersist);
            map[typeof(Double)] = typeof(DoubleDeltaIndexerPersist);
            map[typeof(Decimal)] = typeof(DecimalDeltaIndexerPersist);
            map[typeof(DateTime)] = typeof(DateTimeIndexerPersist);
            map[typeof(String)] = typeof(StringIndexerPersist);
            
            map[typeof(TimeSpan)] = typeof(TimeSpanIndexerPersist);
            
            //other types
            map[typeof(Type)] = typeof(TypeIndexerPersist);
            map[typeof(Byte[])] = typeof(ByteArrayIndexerPersist);
            map[typeof(Image)] = typeof(ImageIndexerPersist);
            //map[typeof(Icon)] = typeof(IconIndexerPersist);
            map[typeof(MemoryStream)] = typeof(MemoryStreamIndexerPersist);
            map[typeof(Guid)] = typeof(GuidIndexerPersist);
            map[typeof(XElement)] = typeof(XElementIndexerPersist);

            //repository types
            map[typeof(Locator)] = typeof(LocatorIndexerPersist);
            map[typeof(BlobStream)] = typeof(BlobStreamIndexerPersist);
            //map[typeof(XKey)] = typeof(XKeyIndexerPersist);
        }

        public static Type GetDefaultPersist(Type type)
        {
            Type result;
            if (map.TryGetValue(type, out result))
                return result;

            if (type.IsEnum)
                return typeof(EnumIndexerPersist<>).MakeGenericType(type);

            if (StructurePrimitives.CanConvertStructToByteArray(type))
                return typeof(StructureIndexerPersist<>).MakeGenericType(type);

            if (type.IsSerializable)
                return typeof(SerializableIndexerPersist<>).MakeGenericType(type);

            //if (type.IsSerializable || (type.IsValueType && type.StructLayoutAttribute.Value != System.Runtime.InteropServices.LayoutKind.Auto))
            //    return typeof(PrimitiveIndexerPersist<>).MakeGenericType(type);

            return null;
        }

        private static bool IsKnownType(Type type)
        {
            return map.ContainsKey(type) ||
                type.IsEnum ||
                StructurePrimitives.CanConvertStructToByteArray(type);
        }

        private static void GetNestedProperties(Type type, string prefix, List<KeyValuePair<Type, string>> nestedProperties)
        {
            if (IsKnownType(type))
            {
                if (prefix != String.Empty)
                    nestedProperties.Add(new KeyValuePair<Type,string>(type, prefix));

                return;
            }

            foreach (PropertyInfo info in type.GetProperties())
            {
                if (info.CanRead && info.CanWrite)
                    GetNestedProperties(info.PropertyType, prefix != String.Empty ? prefix + "." + info.Name : info.Name, nestedProperties);
            }
        }

        public static List<KeyValuePair<Type, string>> GetNestedProperties(Type type)
        {
            List<KeyValuePair<Type, string>> list = new List<KeyValuePair<Type, string>>();
            GetNestedProperties(type, "", list);
            return list;
        }
    }
}
